\documentclass[supercite]{Experimental_Report}

\title{~~~~~~汇编语言程序设计实践~~~~~~}
\author{崔昊阳}
\school{计算机科学与技术学院}
\classnum{CS2104}
\stunum{U2021154514}
\instructor{金良海}
\date{2023年3月10日}

\usepackage{algorithm, multirow}
\usepackage{algpseudocode}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{framed}
\usepackage{mathtools}
\usepackage{subcaption}
\usepackage{xltxtra}
\usepackage{bm}
\usepackage{tikz}
\usepackage{tikzscale}
\usepackage{pgfplots}
\usepackage{caption}
\usepackage{listings}  % 排代码用的宏包
%\usepackage{enumerate}
\lstset{
    language = C,
    backgroundcolor = \color{white},    % 背景色
    basicstyle = \small\ttfamily,           % 基本样式 + 小号字体
    rulesepcolor= \color{gray},             % 代码块边框颜色
    breaklines = true,                  % 代码过长则换行
    keywordstyle = \color{blue}\bfseries,      % 关键字颜色
    commentstyle =\color{green},        % 注释颜色
    stringstyle = \color{red},          % 字符串颜色
    frame = shadowbox,                  % 用（带影子效果）方框框住代码块
    showspaces = false,                 % 不显示空格
    columns = fixed,                    % 字间距固定
    %escapeinside={<@}{@>}              % 特殊自定分隔符：<@可以自己加颜色@>
    morekeywords = {STRUCT,ENDS,DD,DB,DUP},                % 自加新的关键字（必须前后都是空格）
    deletendkeywords = {compile}        % 删除内定关键字；删除错误标记的关键字用deletekeywords删！
}

\pgfplotsset{compat=1.16}

\newcommand{\cfig}[3]{
  \begin{figure}[htb]
    \centering
    \includegraphics[width=#2\textwidth]{images/#1.tikz}
    \caption{#3}
    \label{fig:#1}
  \end{figure}
}

\newcommand{\sfig}[3]{
  \begin{subfigure}[b]{#2\textwidth}
    \includegraphics[width=\textwidth]{images/#1.tikz}
    \caption{#3}
    \label{fig:#1}
  \end{subfigure}
}

\newcommand{\xfig}[3]{
  \begin{figure}[htb]
    \centering
    #3
    \caption{#2}
    \label{fig:#1}
  \end{figure}
}

\newcommand{\rfig}[1]{\autoref{fig:#1}}
\newcommand{\ralg}[1]{\autoref{alg:#1}}
\newcommand{\rthm}[1]{\autoref{thm:#1}}
\newcommand{\rlem}[1]{\autoref{lem:#1}}
\newcommand{\reqn}[1]{\autoref{eqn:#1}}
\newcommand{\rtbl}[1]{\autoref{tbl:#1}}

\algnewcommand\Null{\textsc{null }}
\algnewcommand\algorithmicinput{\textbf{Input:}}
\algnewcommand\Input{\item[\algorithmicinput]}
\algnewcommand\algorithmicoutput{\textbf{Output:}}
\algnewcommand\Output{\item[\algorithmicoutput]}
\algnewcommand\algorithmicbreak{\textbf{break}}
\algnewcommand\Break{\algorithmicbreak}
\algnewcommand\algorithmiccontinue{\textbf{continue}}
\algnewcommand\Continue{\algorithmiccontinue}
\algnewcommand{\LeftCom}[1]{\State $\triangleright$ #1}

\newtheorem{thm}{定理}[section]
\newtheorem{lem}{引理}[section]

\colorlet{shadecolor}{black!15}

\theoremstyle{definition}
\newtheorem{alg}{算法}[section]

\def\thmautorefname~#1\null{定理~#1~\null}
\def\lemautorefname~#1\null{引理~#1~\null}
\def\algautorefname~#1\null{算法~#1~\null}

\begin{document}

\maketitle

\clearpage

\pagenumbering{Roman}

\tableofcontents[level=2]

\clearpage

\pagenumbering{arabic}

\section{程序设计的全程实践}

\subsection{目的与要求}

\begin{enumerate}
	\item 掌握汇编语言程序设计的全周期、全流程的基本方法与技术.
	\item 通过程序调试、数据记录和分析，了解影响设计目标和技术方案的多种因素
\end{enumerate}

\subsection{实验内容}

内容1.1：采用子程序、宏指令、多模块等编程技术设计实现一个较为完整的计算机系统运行状态的监测系统，
给出完整的建模描述、方案设计、结果记录与分析。

内容1.2：初步探索影响设计目标和技术方案的多种因素，主要从指令优化对程序性能的影响，
不同的约束条件对程序设计的影响，不同算法的选择对程序与程序结构的影响，
不同程序结构对程序设计的影响，不同编程环境的影响等方面进行实践。

\subsection{内容1.1的实验过程}

\subsubsection{设计思想}

本程序分成两个模块，分别是主模块mainprog.asm和子程序所在的模块subprog.asm。两个模块间的公共符号是3个子程序的
名称cal\_f, copy\_samples和print\_midf。

主模块中包含主程序和串比较宏mystrcmp。在主程序中，我们首先提示用户输入用户名和密码，接着读入用户输入的用户名和密码，
然后调用串比较宏比较它们是否和正确的用户名和密码相同。如果不相同，重复上述过程3次，3次后依然不同则直接退出程序。
如果相同，则进入数据处理步骤。数据处理时，我们调用subprog.asm中的cal\_f子程序计算$f = (5a + b - c + 100) \div 128$。
根据$f$的值把数据复制到不同存储单元中。如果$f<100$，则复制到LOWF；如果$f=100$，则复制到MIDF；如果$f>100$，则复制到
HIGHF。然后打印出存储在MIDF中的所有数据。最后，程序将进入死循环等待用户输入，输入R则重新进行数据处理和复制，输入Q则退出。
输入其它字符则继续在循环中等待。

串比较宏接受两个待比较字符串的地址和比较结果的存储地址，如果两串相等，
则在传入的存储地址中存入1，否则存入0。字符串比较时，建立两个指针依次遍历两个字符串，如果发现指针所指的两个字符不同，
或者其中一个指针指向0而另一个指针不指向0，则说明两字符串不相同，向结果中传入0后退出即可；
如果遍历到两指针均指向0依然没有出现上述情况，那么说明两字符串相同，向结果中传入1后退出。

数据处理子程序cal\_f用于计算$f = (5a + b - c + 100) \div 128$。它接受一个样本数据结构体的地址参数。cal\_f首先把后续用到的所有寄存器的初始值入栈。接着
把a从栈中取出并赋值给eax，接着使用取地址的方式计算$5\times a+100$。然后使用add和sub指令加上b并减去c。然后使结果
右移7位，并处理溢出情况，以实现除以128的功能。最后我们从栈中弹出所有用到的寄存器的初始值并返回。

数据复制子程序copy\_samples用于把指定的数据复制到指定的地址。它接受源数据的地址和要复制到的地址两个参数。copy\_samples首先把后续用到的所有寄存器的初始值入栈。
由于样本数据结构体有22个字节，所以copy\_samples以双字为单位复制5次，并以字为单位复制1次，这样就完成了一个样本数据结构体的复制。最后我们从栈中弹出所有用到的寄存器的初始值并返回。

数据打印子程序print\_midf用于打印存储在MIDF中的一个样本数据结构体。它接受样本数据结构体的地址。调用printf子程序
打印样本数据结构体的内容并退出。

在本程序中，eax, ebx等一般用于存储值，esi, edi等一般用于存储地址。在本程序中，我们采用简化段定义，在简化段定义中，
段默认合并。


\subsubsection{流程图}

这里一共绘制了主模块mainprog.asm和数据计算子程序cal\_f、数据复制子程序copy\_samples的流程图。流程图如下。

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.65]{images/3.1主程序的流程图.pdf}
		\caption{主模块mainprog.asm的流程图}
	\end{center}
\end{figure}

\begin{figure}[H]
  \begin{minipage}[t]{0.5\linewidth}
		\centering
		\includegraphics[scale=0.8]{images/3.1cal_f的流程图.pdf}
		\caption{子程序cal\_f的流程图}
		\label{}
	\end{minipage}
	\begin{minipage}[t]{0.5\linewidth}
		\centering
		\includegraphics[scale=0.8]{images/3.1copy_samples的流程图.pdf}
		\caption{子程序copy\_samples的流程图}
		\label{}
	\end{minipage}
\end{figure}

\subsubsection{源程序}

实验3.1的源程序有两个。其中用高级伪指令写的程序位于：/实验三/实验三的代码/Project3\_1。
\textbf{不}用高级伪指令写的程序位于：/实验三/实验三的代码/Project3\_1\_2。

\subsubsection{实验记录与分析}

\subsubsubsection{实验环境条件}

\begin{enumerate}
  \item CPU：11th Gen InteI(R) Core(TM) i7-11800H @2.30GHz
  \item 内存：16GB
  \item IDE：WINDOWS11下VS2022社区版
\end{enumerate}

\subsubsubsection{汇编、链接中的情况}

在实验3.1中，汇编、链接未出现任何问题。
在实验3.2中，出现了2个问题，具体如下。
\begin{enumerate}
  \item 问题一：程序在编译过程中，把asm文件按照C语言进行编译，从而报错。\\
        解决方法一：去掉C程序中的\#include<subprog.asm>预编译指令。
  \item 问题二：C语言的结构体大小和汇编中的不一致，导致程序不能正常运行。\\
        解决方法二：C语言中的结构体会自动对齐，需要加上\#pragma pack(1)预编译指令禁用C语言自动对齐。
\end{enumerate}

\subsubsubsection{程序基本功能的验证情况}
首先进行用户名密码验证功能。正确的用户名和密码分别是chy和666。下图是连续输错3次用户名密码的情况。

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.6]{images/3.1用户名密码测试.png}
		\caption{连续输错3次用户名密码，程序直接退出}
	\end{center}
\end{figure}

接着进行其它功能的测试。程序预设的样本数据结构体数组如下。
\begin{lstlisting}[title = 预设的样本数据结构体数组 ,frame=none]
            SAMPLES  STRUCT
              SAMID  DB 6 DUP(0)   ;每组数据的流水号
              SDA   DD  256809     ;状态信息a
              SDB   DD  -1023      ;状态信息b
              SDC   DD   1265      ;状态信息c
              SF    DD   0        ;处理结果f
            SAMPLES  ENDS
            INPUT  SAMPLES  <'1',0,12700,0,?>
                   SAMPLES  <'2',321,432,10,?>
                   SAMPLES  <'3',2400,800,100,?>
                   SAMPLES  <'4',32100654,432,10,?>
                   SAMPLES  <'5',2000,3200,500,?>
\end{lstlisting}
在运行时，依次按下R,R,Q键，这时程序运行的情况如下。
\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.6]{images/3.1数据计算测试.png}
		\caption{依次按下R,R,Q键，程序运行的结果}
	\end{center}
\end{figure}

由上述内容可知，程序运行正常，满足题目的要求。

\subsubsubsection{使用调试工具观察、探究代码的情况}

在本文中，我们使用VS2022的调试工具来研究\textbf{调用数据计算cal\_f子程序前后esp寄存器以及栈的变化情况}。

首先在主模块参数入栈语句设置断点，程序运行至断点处时的寄存器和栈情况如图。我们发现，此时esp寄存器中的值为00AFFE88。
\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.35]{images/3.1调试11.png}
		\caption{调试时的初始状态}
	\end{center}
\end{figure}

进入子程序之后，寄存器和栈情况如图。我们发现，此时esp寄存器中的值为00AFFE80，比上图中的esp值小了8。
同时，我们观察到栈中的值也发生了变化。这是因为调用子程序之前，双字参数进栈，call指令使eip进栈，这样就使得esp减小了8。

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.35]{images/3.1调试12.png}
		\caption{刚进入子程序}
	\end{center}
\end{figure}

接着我们观察从子程序中跳出的过程，下图是子程序运行至ret时的情况，注意到esp的值为00AFFE80。

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.35]{images/3.1调试13.png}
		\caption{从子程序中跳出前}
	\end{center}
\end{figure}

跳出子程序后，我们观察到esp的值变为00AFFE84。这是因为eip出栈，esp增加4。
\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.35]{images/3.1调试14.png}
		\caption{跳出子程序}
	\end{center}
\end{figure}

由于我们使用的是C模式，所以我们需要在主程序中释放参数空间，即使esp加4。到此，子程序调用过程完毕。
\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.35]{images/3.1调试15.png}
		\caption{跳出子程序}
	\end{center}
\end{figure}


\subsubsubsection{其他}

\subsection{内容1.2的实验过程}

\subsubsection{指令优化对程序的影响}

在实验二中，我们对样品数据计算和样品数据复制的程序进行了汇编指令级的\textbf{3种优化}。这三种优化分别是：
\textbf{字节复制转换成双字复制，除法用移位实现，加减法用取地址实现}。对于每一种优化，我们使用$10^9$组数据运行了10次，用得到的数据进行了\textbf{正态分布的估计}，
得到的表格和图像如下。

\begin{table}[!ht]
  \caption{各种优化后的平均运行时间表}	\centering
  \begin{tabular}{c c}
  \toprule
      \textbf{优化种类} & \textbf{平均运行时间(s)} \\\hline
      无优化 & 11.535 \\
      字节复制->双字复制 & 5.124 \\
      除法->移位 & 4.949 \\
      加减法->取地址 & 4.888 \\
  \bottomrule
  \end{tabular}
\end{table}

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.45]{images/2无优化运行时间.png}
		\caption{无优化运行时间}
	\end{center}
\end{figure}

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.45]{images/2各种优化运行时间对比.png}
		\caption{各种优化运行时间对比}
	\end{center}
\end{figure}

根据表格和图像，我们可以看出，\textbf{第一种优化效果最为明显，第二种优化效果比较明显，第三种优化效果不明显}。

\subsubsection{约束条件、算法与程序结构的影响}

我们首先探究不同约束条件和算法对验证用户名密码是否正确的串比较程序的影响。我们分别研究了使用字节比较和字比较时程序的差异和
首先比较串长是否相等和不进行此操作时程序的差异。当两字符串完全相同时，串比较程序程序打印出"OK!"，
否则打印出"Incorrect Password!"。其运行效果如下图所示。
\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.6]{images/串比较程序匹配.png}
		\caption{两字符串相等}
	\end{center}
\end{figure}

\begin{figure}[H]
	\begin{center}
		\includegraphics[scale=0.6]{images/串比较程序不匹配.png}
		\caption{两字符串不相等}
	\end{center}
\end{figure}

如果按字节比较，则程序的设计思路如下。首先设置两个指针指向两字符串开头，接着同时比较指针指向的字符。若两指针指向的字符都不为0且不相同，
说明字符串不相等，直接停止比较即可。若两指针其中一个指向0，另一个不为0，说明两字符串串长不同，不相等，直接停止比较即可。若两指针指向的字符
相同且均不为0，则指针后移比较下一个字符。若两指针同时指向0，说明比较结束，两字符串相同。

如果按字比较，则需要先计算串长。串长可以通过两种方式获得。一种是在比较前遍历一遍字符串，另一种是要求用户在输入字符串时同时输入串长。
获得串长后，计算出需要比较的字的个数，之后进行循环比较即可。如果发现串长不相同，则字符串一定不相等，可以直接停止比较。如果串长相同且为偶数，那么循环串长的$\frac{1}{2}$次，
比较规则同上。如果串长相同且为奇数，则注意最后一个字符须改用字节比较。

通过分析，我们发现，\textbf{当使用字节比较时，可以不用首先计算串长；但使用字比较时，必须先计算两字符串的串长}，
并应当在末尾不足一个字时换用字节比较，否则会发生访问越界的现象。所以，尽管使用字比较一次比较位数较多，循环次数较少，但因为
计算串长需要遍历一遍字符串，所以在比较字符串时，\textbf{其效率不优于字节比较}。如果在比较之前先计算串长，那么当两个字符串长度相同时，
需要遍历两遍字符串才能确定字符串是否相同；当字符串长度不同时，则只需遍历一遍。但在比较之前不计算串长时，无论字符串长度是否相同，
均只需遍历一遍。所以\textbf{不进行串长比较的串比较算法较优}。当然，如果要求用户在输入字符串时同时输入串长，那么先比较
串长的算法更优，但一般不会要求用户这么做。

接着我们探究不同程序结构对计算机系统运行状态监测系统的影响。我们使用纯汇编和C语言、汇编混合编程的方式编写了计算机系统运行状态监测系统。
在下文中，我们主要阐述两者的不同点。

在使用纯汇编分模块编写应用时，模块中其它模块要用到的变量\textbf{需要用public声明}。当一个模块要用到其他模块中定义的变量时，
\textbf{需用extern声明}。在使用C语言调用汇编语言子程序时，需要在C语言程序中进行函数声明，接着直接调用汇编子程序即可。

\subsubsection{编程环境的影响}

\subsection{小结}

通过完成上述实验，我对汇编语言的常用指令、汇编语言子程序调用过程。汇编语言模块化编程以及高级语言的底层机制有了更深入地了解。

% \subsubsection{思考题}

\subsubsection{经验教训}
\begin{enumerate}
  \item C语言的结构体会自动对齐，所以在汇编和C语言混合编程时，要禁用自动对齐。
  \item 在汇编和C语言混合编程时，不用include汇编的文件。
  \item 使用右移代替除法时，要考虑溢出情况。
  \item 当子程序需要使用很多寄存器时，可以把寄存器的初始值入栈保护。
  \item printf,scanf等函数在调用结束后会改变一些寄存器的值。
\end{enumerate}

\newpage
\section{利用汇编语言特点的实验}

\subsection{目的与要求}

\subsection{实验内容}

\subsection{实验过程}

\subsubsection{中断处理程序}

\subsubsubsection{设计思想与实验方法}

\subsubsubsection{记录与分析}

\subsubsection{反跟踪程序}

\subsubsubsection{设计思想与实验方法}

\subsubsubsection{反跟踪效果的验证}

\subsubsubsection{跟踪与破解程序}

\subsubsection{指令优化及程序结构}

\subsubsubsection{实验方法}

\subsubsubsection{特定指令及程序结构的效果}

\subsection{小结}

\newpage
\section{工具环境的体验}

\subsection{目的与要求}

\subsection{实验过程}

\subsubsection{WINDOWS10下VS2019等工具包}

\subsubsection{DOSBOX下的工具包}

\subsubsection{QEMU下ARMv8的工具包}

\subsection{小结}

\newpage
\section{参考文献}
[1]许向阳. x86汇编语言程序设计. 武汉：华中科技大学出版社，2020

[2]许向阳. 80X86汇编语言程序设计上机指南. 武汉：华中科技大学出版社， 2007

[3]王元珍，曹忠升，韩宗芬.  80X86 汇编语言程序设计. 武汉：华中科技大学出版社，2005

[4]汇编语言课程组. 《汇编语言程序设计实践》任务书与指南，2023

\end{document}

% \ref{fig1-1}

% \begin{enumerate}
% \renewcommand{\labelenumi}{\theenumi)}
% 	\item C++
% 	\item Java
% 	\item HTML
% \end{enumerate}

% \begin{figure}[htb] % here top bottom
% 	\begin{center}
% 		\includegraphics[scale=0.80]{images/1-1.pdf}
% 		\caption{网页整体框架举例}
% 		\label{fig1-1}
% 	\end{center}
% \end{figure}


% \begin{table}
% 	\begin{center}
% 		\setlength{\tabcolsep}{2.0mm}
% 		\caption{Mean and standard deviation of estimation error (Euler angles) on Pandora. The best performance is in \textbf{bold}.}
% 		\label{table2}
% 		\begin{tabular}{c|ccccc}
% 			\hline
% 			Method    			        & Data               & Pitch         & Roll           & Yaw              & Accuracy\\
% 			\hline
% 			\hline			
% 			\multirow{5}{*}{POSEidon}   & Depth              & 6.5 $\pm$ 6.6  & 5.4 $\pm$ 5.1  & 10.4 $\pm$ 11.8  & 0.646\\
% 			& FfD              	 & 6.8 $\pm$ 7.0  & 5.7 $\pm$ 5.7  & 10.5 $\pm$ 14.6  & 0.647\\
% 			& Gray-level         & 7.1 $\pm$ 6.6  & 5.6 $\pm$ 5.8  & 9.0  $\pm$ 10.9  & 0.639\\
% 			& Depth + FfD	     & 5.6 $\pm$ 5.0  & 4.9 $\pm$ 5.0  & 9.8  $\pm$ 13.4  & 0.698\\
% 			& Depth + FfD + MI   & 5.7 $\pm$ 5.6  & 4.9 $\pm$ 5.1  & 9.0  $\pm$ 11.9  & 0.715\\
% 			\hline
% 			DRF                         & Depth              & 6.2 $\pm$ 9.5  & 4.6 $\pm$ 6.7  & 9.3  $\pm$ 14.6  & --\\
% 			\hline
% 			\multirow{3}{*}{Ours}   	& Depth              & 5.9 $\pm$ 6.2  & 4.5 $\pm$ 4.9  & 8.8  $\pm$ 10.9  & 0.666\\
% 			& RGB                & 5.5 $\pm$ 5.3  & 4.4 $\pm$ 5.5  & 8.6  $\pm$ 9.3   & 0.698\\
% 			& RGB + Depth        & 5.0 $\pm$ 4.8  & 4.3 $\pm$ 4.9  & 8.1  $\pm$ 8.3   & \textbf{0.737}\\
% 			\hline
% 		\end{tabular}
% 	\end{center}
% \end{table}


% \ref{alg:1}，\ref{alg:2}。

% \begin{shaded*}\begin{alg}{一个复杂算法}
% 		\label{alg:1}
% 		\begin{algorithmic}
% 			\Input Two numbers $a$ and $b$
% 			\Output The sum of $a$ and $b$
% 			\Procedure{A-Plus-B}{$a, b$}
% 			\If $a = 0$
% 			\State \Return $b$
% 			\EndIf
% 			\State $res \gets 0$
% 			\While{$b \neq 0$}
% 			\State Increase $res$ by $1$
% 			\State $b \gets b - 1$
% 			\EndWhile
% 			\State \Return $res$
% 			\EndProcedure
% 		\end{algorithmic}
% \end{alg}\end{shaded*}

% \begin{algorithm}[h] 
% 	\caption{一个更复杂算法}
% 	\begin{algorithmic}[1]
% 		\State Initialization: $I_{xy}$, $z_{f}=Zeros(128, 128)$; 
% 		\For{$0\leq n \textless N$}
% 		\State $i=\lfloor x_n \rfloor+64$, $j=\lfloor y_n \rfloor + 64$
% 		\If{$z_n<0$ and $|z_n|>|z_{f}(i,j)|$};
% 		\State $z_{f}(i,j)=z_n$;
% 		\EndIf
% 		\State $I_{xy}(i,j)=z_{f}(i,j)$;
% 		\EndFor 
% 	\end{algorithmic}\label{alg:2}
% \end{algorithm}


% \nocite{*} %% 作用是不对文献进行引用，但可以生成文献列表

% \bibliographystyle{Experimental_Report}
% \bibliography{Experimental_Report}
% \setcounter{secnumdepth}{0}
% \appendix